package com.leyou.search.service;

import com.leyou.item.pojo.Brand;
import com.leyou.item.pojo.Category;
import com.leyou.item.pojo.SpecParam;
import com.leyou.search.clients.BrandClient;
import com.leyou.search.clients.CategoryClient;
import com.leyou.search.clients.SpecClient;
import com.leyou.search.pojo.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.utils.SearchRequest;
import com.leyou.search.utils.SearchResult;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchService {


    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private BrandClient brandClient;

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private SpecClient specClient;


    /**
     * 查询商品的查询条件来查询商品，返回结果首先是个分页结果，在分页结果的基础上加了，品牌集合，分类集合，还有可搜索规格参数集合
     * @param searchRequest
     * @return
     */
    public SearchResult queryGoodsByPage(SearchRequest searchRequest) {

        //自定义查询的构建工具
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        String key = searchRequest.getKey();

        if (!StringUtils.isNotBlank(key)){
            return null;
        }

        //给工具添加查询条件（查询条件的构造被单独抽取）
        QueryBuilder query = buildBasicQueryWithFilter(searchRequest);

        queryBuilder.withQuery(query);

        //给工具添加分页条件 -1
        queryBuilder.withPageable(PageRequest.of(searchRequest.getPage()-1,searchRequest.getSize()));


        //添加聚合条件
        // 1.3、聚合
        String categoryAggName = "category"; // 商品分类聚合名称
        String brandAggName = "brand"; // 品牌聚合名称

        //添加聚合条件
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field("cid3"));
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field("brandId"));


        //执行查询获取到分页结果
        AggregatedPage<Goods> search = (AggregatedPage<Goods>) this.goodsRepository.search(queryBuilder.build());

        LongTerms categoryTerms = (LongTerms) search.getAggregation(categoryAggName);

        //获得分类桶
        List<LongTerms.Bucket> categoryTermsBuckets = categoryTerms.getBuckets();


        List<Long> cids = new ArrayList<>();

        categoryTermsBuckets.forEach(bucket -> {
            cids.add(bucket.getKeyAsNumber().longValue());
        });


        LongTerms brandTerms = (LongTerms) search.getAggregation(brandAggName);

        //获取品牌的桶
        List<LongTerms.Bucket> brandTermsBuckets = brandTerms.getBuckets();

        List<Long> brandIds = new ArrayList<>();

        brandTermsBuckets.forEach(bucket -> {
            brandIds.add(bucket.getKeyAsNumber().longValue());
        });

        //TODO 获取到了品牌id集合和分类id集合，需要把id对应对象，又要跨服务


        List<Brand> brands = new ArrayList<>();
        //解析品牌
        brandIds.forEach(brandId->{
            brands.add( this.brandClient.queryBrandById(brandId));
        });

        //解析分类
        List<String> names = this.categoryClient.queryNameByIds(cids);

        List<Category> categories = new ArrayList<>();
        //把查询到的name和id要挨个品牌

        for (int i = 0; i < names.size(); i++) {
            Category c = new Category();
            c.setId(cids.get(i));
            c.setName(names.get(i));
            categories.add(c);
        }

        //过滤的规格参数展示，map将有两个key，一个是k，k的值就是规格过滤字段的名称，另外options，就是对个过滤字段对应的数组
        List<Map<String, Object>> specs = null;


        if (categories.size()==1){
            //构造返回的规格参数结果
            specs = getSpecs(categories.get(0).getId(),query);
        }

        //构造pageResult结果
        return new SearchResult(search.getTotalElements(),new Long(search.getTotalPages()),search.getContent(),categories,brands,specs);
    }

    //用来构造当前分类的所有的可搜索规格参数的信息,并且要把查条件传入，要根据查询条件查到的数据再来聚合而获取他们的规格参数
    private List<Map<String, Object>> getSpecs(Long id,  QueryBuilder query) {

        List<Map<String, Object>> specs = new ArrayList<>();


        //所有的可搜索的规格参数
        List<SpecParam> specParams = this.specClient.querySpecParam(null, id, true,null);


        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //先执行查询，从查询到的结果中进行聚合
        queryBuilder.withQuery(query);


        //添加聚合条件
        for (SpecParam specParam : specParams) {
            //获取可搜索规格参数的名字，以此为聚合名字
            String aggName = specParam.getName();
            queryBuilder.addAggregation(AggregationBuilders.terms(aggName).field("specs."+aggName+".keyword"));
        }

        //执行查询
        AggregatedPage<Goods> search = (AggregatedPage<Goods>) this.goodsRepository.search(queryBuilder.build());

        //解析聚合结果

        for (SpecParam specParam : specParams) {
            String aggName = specParam.getName();

            StringTerms stirngTerms = (StringTerms) search.getAggregation(aggName);

            List<StringTerms.Bucket> buckets = stirngTerms.getBuckets();


            List<String> values = new ArrayList<>();

            for (StringTerms.Bucket bucket : buckets) {
                values.add(bucket.getKeyAsString());
            }

            Map<String, Object> spec = new HashMap<>();
            spec.put("k",aggName);
            spec.put("options",values);

            specs.add(spec);
        }


        return specs;
    }



    // 构建基本查询条件
    private QueryBuilder buildBasicQueryWithFilter(SearchRequest request) {
        //创建一个布尔查询
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 基本查询条件
        queryBuilder.must(QueryBuilders.matchQuery("all", request.getKey()).operator(Operator.AND));
        // 过滤条件构建器
        BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();
        // 整理过滤条件
        Map<String, String> filter = request.getFilter();
        for (Map.Entry<String, String> entry : filter.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            // 商品分类和品牌可以直接查询不需要拼接
            if (key != "cid3" && key != "brandId") {
                key = "specs." + key + ".keyword";
            }
            // 字符串类型，进行term查询
            filterQueryBuilder.must(QueryBuilders.termQuery(key, value));
        }
        // 添加过滤条件
        queryBuilder.filter(filterQueryBuilder);
        return queryBuilder;
    }
}
